'use strict';
const log = require('@dou-gong-cli/log');
const command = require('@dou-gong-cli/command');
const Package = require('@dou-gong-cli/package');
const { sleep, spinnerStart, exec, execAsync } = require('@dou-gong-cli/utils');
const { readdirSync, writeFileSync } = require('fs');
const semver = require('semver');
const { getProjectTemplate } = require('./get-project-template');

const inquirer = require('inquirer');
const { emptyDirSync, ensureDirSync, copySync } = require('fs-extra');
const ejs = require('ejs');
const { glob } = require('glob');
const path = require('path');
const { TEMPLATE_TYPE } = require('./const');

/** 初始化类型 */
const INIT_TYPE = {
  PROJECT: 'project',
  COMPONENT: 'component'
};

const WHITE_COMMAND = ['npm', 'yarn', 'cnpm', 'pnpm'];

class InitCommand extends command {
  init() {
    this.projeckName = this._argv[0];
    this.force = !!this._argv[1]['force'];
  }

  async exec() {
    // 1. 准备阶段
    // 2. 下载阶段
    // 3. 安装阶段
    log.verbose('init', '开始初始化');
    try {
      const projectInfo = await this.prepare();
      if (projectInfo) {
        log.verbose('init projectInfo', projectInfo);
        this.projectInfo = projectInfo;
        await this.downloadTemplate();
        await this.installTemplate();
      }
    } catch (e) {
      log.error('initCommand', e.message);
    }
  }

  /** init准备阶段 */
  async prepare() {
    // 1. 判断目录是否为空
    //    1.1 为空，询问是否继续创建
    // 2. 是否强制init
    // 3. 选择创建 项目 \ 组件
    // 4. 获取项目的基本信息
    let template;
    try {
      template = await getProjectTemplate();
    } catch (error) {
      log.error('initCommand', '获取项目模板失败');
      throw error;
    }
    if (!template || template.length === 0) {
      throw new Error('项目模板不存在');
    }
    this.template = template;

    const localPath = process.cwd();
    if (!this.isCwdEmpty(localPath)) {
      let isContinue = false;
      if (!this.force) {
        isContinue = (
          await inquirer.prompt([
            {
              name: 'isContinue',
              type: 'confirm',
              default: false,
              message: '当前文件夹不为空，是否继续创建项目？'
            }
          ])
        ).isContinue;

        if (!isContinue) {
          return;
        }
      }

      if (isContinue || this.force) {
        const { confirmDelete } = await inquirer.prompt([
          {
            name: 'confirmDelete',
            type: 'confirm',
            message: '是否清空当目录下的所有文件？'
          }
        ]);
        if (confirmDelete) {
          emptyDirSync(localPath);
        }
      }
    }
    return this.getProjectInfo();
  }

  /** 下载模板 */
  async downloadTemplate() {
    // 1. 通过项目模板API获取项目模板信息
    //    1.1 通过egg.js搭建一套后端API
    //    1.2 通过npm存储项目模板
    //    1.3 通过项目模板信息存储到mongodb数据库中
    //    1.4 通过egg.js获取mongodb中的数据并通过API返回
    const { projectTemplate } = this.projectInfo;
    const templateInfo = this.template.find(
      (t) => t.npmName === projectTemplate
    );
    /** 模板信息 */
    this.templateInfo = templateInfo;
    const { npmName, version } = templateInfo;
    const homePath = process.env.DG_CLI_HOME_PATH;
    const targetPath = path.resolve(homePath, 'template');
    const storeDir = path.resolve(homePath, 'template', 'node_modules');
    const templateNpm = new Package({
      targetPath,
      storeDir,
      packageName: npmName,
      packageVersion: version
    });

    if (!(await templateNpm.exists())) {
      const spinner = spinnerStart({ text: '正在下载模板...' });
      // await sleep(1000);
      try {
        await templateNpm.install();
        spinner.stop();
        this.templateNpm = templateNpm;
        log.success('initCommand', '下载模板成功');
      } catch (error) {
        spinner.stop();
        log.error('initCommand', '下载模板失败');
        throw error;
      }
      spinner.stop();
    } else {
      const spinner = spinnerStart({ text: '正在更新模板...' });
      // await sleep(1000);
      try {
        await templateNpm.update();
        spinner.stop();
        this.templateNpm = templateNpm;
        log.success('initCommand', '更新模板成功');
      } catch (error) {
        spinner.stop();
        log.error('initCommand', '更新模板失败');
        throw error;
      }
    }
  }

  /** 安装模板 */
  async installTemplate() {
    if (!this.templateInfo) {
      throw new Error('项目模板不存在！');
    }
    if (this.templateInfo.type === TEMPLATE_TYPE.normal) {
      // 标准安装
      await this.installNormalTemplate();
    } else if (this.templateInfo.type === TEMPLATE_TYPE.custom) {
      // 自定义安装
      await this.installCustomTemplate();
    } else {
      throw new Error('项目模板类型无法识别！');
    }
  }

  /** 检查命令是否是白名单命令 */
  checkCommand(command) {
    if (WHITE_COMMAND.includes(command)) {
      return command;
    } else {
      throw new Error(`${command} 不在命令白名单中`);
    }
  }

  async execCommand(command, errMsg) {
    let execResult;
    if (command) {
      const initCmd = command.split(' ');
      const cmd = this.checkCommand(initCmd[0]);
      const args = initCmd.slice(1);
      execResult = await execAsync(cmd, args, {
        stdio: 'inherit',
        cwd: process.cwd()
      });
    }
    if (execResult !== 0) {
      throw new Error(errMsg);
    }
  }

  async ejsRender(options) {
    const currentDir = process.cwd();
    let fileList = await glob('**', {
      cwd: currentDir,
      ignore: options.ignore,
      nodir: true
    });
    Promise.all(
      fileList.map((file) => {
        const filePath = path.resolve(currentDir, file);
        return new Promise((resolve, reject) => {
          ejs.renderFile(filePath, this.projectInfo, (err, result) => {
            if (err) {
              reject(err);
            } else {
              writeFileSync(filePath, result);
              resolve(result);
            }
          });
        });
      })
    );
    return fileList;
  }

  async installNormalTemplate() {
    // 拷贝模板代码至当前目录
    // 安装依赖
    // 启动服务
    let spinner = spinnerStart({ text: '正在安装模板...' });
    try {
      const templatePath = path.resolve(
        this.templateNpm.cacheFilePath,
        'template'
      );
      const targetPath = process.cwd();
      ensureDirSync(templatePath);
      ensureDirSync(targetPath);
      copySync(templatePath, targetPath);
      spinner.stop();
      log.success('initCommand', `模板：${this.templateInfo.name} 安装成功`);
    } catch (error) {
      spinner.stop();
      log.error('initCommand', '安装normal模板失败');
      throw error;
    }
    const ignore = ['node_modules/**'];
    let fileList = await this.ejsRender({ ignore });
    log.verbose('fileList', fileList);
    const { initCommand, startCommand } = this.templateInfo;
    await this.execCommand(
      initCommand,
      `项目模板:${this.templateInfo.name} 依赖安装失败`
    );
    await this.execCommand(
      startCommand,
      `项目模板:${this.templateInfo.name} 启动服务失败`
    );
  }

  async installCustomTemplate() {}

  /** 获取项目信息 */
  async getProjectInfo() {
    // 1. 选择创建项目或者组件
    // 2. 获取项目基本信息
    // return 项目基本信息
    let projectInfo = {};
    const { type } = await inquirer.prompt([
      {
        name: 'type',
        type: 'list',
        message: '请选择初始化类型',
        default: INIT_TYPE.PROJECT,
        choices: [
          { name: '项目', value: INIT_TYPE.PROJECT },
          { name: '组件', value: INIT_TYPE.COMPONENT }
        ]
      }
    ]);

    if (type === INIT_TYPE.PROJECT) {
      const project = await inquirer.prompt([
        {
          name: 'projectName',
          message: '请输入项目名称',
          type: 'input',
          default: '',
          validate: function (v) {
            // 1. 首字母必须是英文
            // 2. 尾字母必须是英文和数字，不能是字符
            // 3. 字符仅允许“-_”
            const isVaidate =
              /^[a-zA-Z]+([-][a-zA-Z][a-zA-Z0-9]*|[_][a-zA-Z][a-zA-Z0-9]*|[a-zA-Z0-9])*$/.test(
                v
              );
            let done = this.async();
            setTimeout(function () {
              if (!isVaidate) {
                done('请输入合法的项目名称');
                return;
              }
              done(null, true);
            }, 0);
          },
          filter: (v) => v
        },
        {
          name: 'projectVersion',
          message: '请输入项目初始版本号',
          type: 'input',
          default: '',
          validate: function (v) {
            let done = this.async();
            setTimeout(function () {
              if (!semver.valid(v)) {
                done('请输入合法的版本号');
                return;
              }
              done(null, true);
            }, 0);
          },
          filter: (v) => semver.valid(v) || v
        },
        {
          name: 'projectTemplate',
          type: 'list',
          message: '请选择项目模板',
          choices: this.createTemplateChoice()
        }
      ]);
      projectInfo = {
        type,
        ...project
      };
    } else if (type === INIT_TYPE.COMPONENT) {
    }
    // 格式化项目名称
    if (projectInfo.projectName) {
      projectInfo.className = require('kebab-case')(
        projectInfo.projectName
      ).replace(/^-/, '');
    }
    if (projectInfo.projectVersion) {
      projectInfo.version = projectInfo.projectVersion;
    }
    return projectInfo;
  }

  /** 当前目录是否为空 */
  isCwdEmpty(path) {
    let fileList = readdirSync(path);
    fileList = fileList.filter(
      (file) => !file.startsWith('.') && ['node_modules'].indexOf(file) < 0
    );
    return !fileList || fileList.length <= 0;
  }

  /** 创建模板选项 */
  createTemplateChoice() {
    return this.template.map((item) => ({
      value: item.npmName,
      name: item.name
    }));
  }
}

function init(argv) {
  return new InitCommand(argv);
}

module.exports = init;
